package com.bluexmicro.ota.ui

import android.content.Context
import android.graphics.Color
import android.graphics.Rect
import android.util.Log
import android.util.SparseArray
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bluexmicro.android.ota.entity.OtaState
import com.bluexmicro.module_componment.file.FileResult
import com.bluexmicro.ota.databinding.ItemSelectedFileBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class SelectedFileAdapter(
    callback: FileDiffCallback = FileDiffCallback(),
    private val onItemRemove: (Context, FileResult) -> Unit
) : ListAdapter<FileResult, FileViewHolder>(callback) {

    private var updating: Boolean = false
    private val otaStateArray = SparseArray<OtaState>()
    private var dotIndex = 0 //0~3
    private val dots = listOf(" ", ".", "..", "...")

    suspend fun updateState(otaState: OtaState) = withContext(Dispatchers.IO) {
        otaStateArray.put(otaState.index, otaState)
        if (otaState.index in currentList.indices) {
            val payloads = mutableListOf<MyPayload>()
            payloads.add(MyPayload.Progress(otaState.progress))
            payloads.add(getMyPayload(otaState))
            withContext(Dispatchers.Main) {
                notifyItemChanged(otaState.index, payloads.toList())
            }
        }
    }

    private fun getMyPayload(otaState: OtaState): MyPayload {
        return if (otaState.hasResult()) {
            if (otaState.errorMessage.isNullOrEmpty()) {
                MyPayload.Success(
                    otaState.startTimeMillis,
                    otaState.endTimeMillis
                )
            } else {
                MyPayload.Failed(otaState.errorMessage!!, otaState.progress)
            }
        } else {
            dotIndex = (dotIndex + 1) % 4 //0,1,2,3
            MyPayload.Updating("Updating${dots[dotIndex]}")
        }
    }

    private fun getDesc(index: Int): String? {
        val state: OtaState? = otaStateArray.get(index)
        return state?.run {
            getMyPayload(this)
        }?.value
    }

    private fun getProgress(index: Int): String? {
        return if (updating) {
            val state: OtaState? = otaStateArray.get(index)
            return state?.run {
                "${(progress * 100).toInt()}%"
            }
        } else {
            null
        }
    }

    fun setUpdating(updating: Boolean, needRefresh: Boolean = false) {
        this.updating = updating
        if (updating) {
            otaStateArray.clear()
        }
        if (needRefresh) notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {
        val binding =
            ItemSelectedFileBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return FileViewHolder(binding)
    }

    override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
        val selectedFile = currentList[position]
        holder.binding.apply {
            fileNameTx.text = selectedFile.file.run {
                path.getBinName()
            }
            addressTx.text = if (selectedFile.address != null) {
                "Address: 0x${selectedFile.address!!.toString(16)}"
            } else {
                "Address: default"
            } + " (size: ${selectedFile.file.length().toInt().toFileSize()})"
            removeBtn.setOnClickListener {
                onItemRemove.invoke(it.context, selectedFile)
            }
            removeBtn.visibility = if (updating) View.GONE else View.VISIBLE
            val progress = getProgress(position) ?: ""
            progressTx.visibility = if (updating) View.VISIBLE else View.GONE
            progressTx.text = progress
            val desc = getDesc(position)
            descTx.visibility = if (desc.isNullOrEmpty()) View.GONE else {
                descTx.text = desc
                View.VISIBLE
            }
        }
    }

    override fun onBindViewHolder(
        holder: FileViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position)
        } else {
            for (payloadList in payloads) {
                if (payloadList is List<*>) {
                    for (payload in payloadList) {
                        if (payload is MyPayload) {
                            handleMyPayload(payload, holder)
                        }
                    }
                    break
                }
            }
        }
    }

    private fun handleMyPayload(myPayload: MyPayload, holder: FileViewHolder) {
        when (myPayload) {
            is MyPayload.Failed -> {
                holder.binding.descTx.apply {
                    if (visibility == View.GONE) visibility = View.VISIBLE
                    setTextColor(Color.RED)
                    text = myPayload.value
                }
            }
            is MyPayload.Progress -> {
                holder.binding.progressTx.apply {
                    if (visibility == View.GONE) visibility = View.VISIBLE
                    setTextColor(Color.BLUE)
                    text = myPayload.value
                }
            }
            is MyPayload.Success -> {
                holder.binding.descTx.apply {
                    if (visibility == View.GONE) visibility = View.VISIBLE
                    setTextColor(Color.GREEN)
                    text = myPayload.value
                }
            }
            is MyPayload.Updating -> {
                holder.binding.descTx.apply {
                    if (visibility == View.GONE) visibility = View.VISIBLE
                    setTextColor(Color.BLUE)
                    text = myPayload.value
                }
            }
        }
    }

    override fun onCurrentListChanged(
        previousList: MutableList<FileResult>,
        currentList: MutableList<FileResult>
    ) {
        if (previousList.size != currentList.size) {
            otaStateArray.clear()
        }
        super.onCurrentListChanged(previousList, currentList)
    }


}

class FileViewHolder(val binding: ItemSelectedFileBinding) : RecyclerView.ViewHolder(binding.root)

class SpaceItemDecoration(
    private val topDp: Int = 0,
    private val rightDp: Int = 0,
    private val bottomDp: Int = 0,
    private val leftDp: Int = 0,
) : RecyclerView.ItemDecoration() {


    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        outRect.top = dp2px(view.context, topDp).toInt()
        outRect.right = dp2px(view.context, rightDp).toInt()
        outRect.bottom = dp2px(view.context, bottomDp).toInt()
        outRect.left = dp2px(view.context, leftDp).toInt()

    }

    fun dp2px(context: Context, dp: Int): Float = dp * context.resources.displayMetrics.density
}

class FileDiffCallback : DiffUtil.ItemCallback<FileResult>() {
    override fun areItemsTheSame(oldItem: FileResult, newItem: FileResult): Boolean {
        return oldItem == newItem
    }

    override fun areContentsTheSame(oldItem: FileResult, newItem: FileResult): Boolean {
        //为了更新进度，这里默认为内容不一样
        return true
    }
}

fun Int.toFileSize(): String {
    val KB = 1024
    val MB = 1024 * 1024
    val GB = 1024 * 1024 * 1024
    return when (this) {
        in 0 until KB -> {
            "$this" + "B"
        }
        in KB until MB -> {
            "${this / KB}KB"
        }
        in MB until GB -> {
            "${this / MB}MB"
        }
        else -> {
            "${this / GB}GB"
        }
    }
}

fun String.getBinName(): String {
    val index = this.lastIndexOf('/')
    return if (index in this.indices) {
        this.removeRange(0..index)
    } else {
        this
    }
}

sealed class MyPayload(
    val value: String
) {

    class Progress(progress: Float) : MyPayload("${(progress * 100).toInt()}%")
    class Success(startTimeMillis: Long, endTimeMillis: Long) :
        MyPayload("成功（用时${(endTimeMillis - startTimeMillis) / 1000}秒)")

    class Failed(errorMsg: String, progress: Float) :
        MyPayload("失败（${(progress * 100).toInt()}%）: $errorMsg")

    class Updating(value: String) : MyPayload(value)

}
